﻿/*
 * XapExplorer, global object/singleton like
 * manages applications load, run, stop, activate,...
 * 
 * created by: asida@centrum.cz
 * 
 * initial implementation: 6.12.2011
 * last updated:
 */
using System;
//using System.Net;
//using System.Reflection;
using System.Windows;
//using System.Windows.Resources;
using System.Windows.Controls;
//using System.Windows.Documents;
//using System.Windows.Ink;
//using System.Windows.Input;
//using System.Windows.Media;
//using System.Windows.Media.Animation;
//using System.Windows.Shapes;
//using System.Windows.Threading;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Collections.ObjectModel;
using uhul.Controls;

namespace uhul
{

  public partial class XapExplorer
  {



    private static ObservableCollection<InteractiveApp> _applications;
    private static Dictionary<string, InteractiveApp> _appValidator;

    public delegate void ResizeTabButtonEventArgs(object sender, ResizeEventArgs e); 
    public static event ResizeTabButtonEventArgs ResizeTabButtons;

    //private static DispatcherTimer timer;
    private static XapExplorer _instance;

    static XapExplorer()
    {
      _appValidator = new Dictionary<string, InteractiveApp>();
      _applications = new ObservableCollection<InteractiveApp>();
      RunningApplications = new ReadOnlyObservableCollectionPlus<InteractiveApp>(_applications);
      RunningApplications.CollectionChanged += new NotifyCollectionChangedEventHandler(zmenaPoctuAplikaci);

      _pendingCommands = new Dictionary<string, Command>();

      //this is for communication between apps...
      Environment = new Dictionary<string, string>();
      //Commands = CommandStack.Create();
      Commands = new CommandStack();

      //init commands
      Commands.CollectionChanged += new NotifyCollectionChangedEventHandler(processCommand);
      Environment.Add("testVal", "toto je testovaci hodnota pro overeni spojeni s aplikaci");
    }





    /// <summary>
    /// receives commands from running applications from Commands_CollectionChanged event
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private static void processCommand(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {

      for (int i = 0; i < e.NewItems.Count; i++)
      {
        Command c = Command.Parse((string)e.NewItems[i]);
        if ((int)c.CommandType < 255)
        {
          processSystemCommand(c);
          //e.NewItems.RemoveAt(i);
        }

        //else try resolve send non system and non state change msg to app that is not running
        //by run app command and save command into pending
        else if (!_appValidator.ContainsKey(c.TargetApplicationName) && (int)c.CommandType >= 500)
        {
          //save/replace command to pending queue
          if(!_pendingCommands.ContainsKey(c.TargetApplicationName))
            _pendingCommands.Add(c.TargetApplicationName, c);
          else
            _pendingCommands[c.TargetApplicationName] = c;

          //run app
          RunApplication(c.TargetApplicationName);
          //e.NewItems.RemoveAt(i);
        }

        ////if command is not activate app or activate as previous, process behaviours
        //if (c.CommandType != CommandTypeEnum.ActivateApp && c.CommandType != CommandTypeEnum.ActivateAppAsPrevious)
        //  //process command behaviour
        //  switch (c.CommandVisualBehaviour)
        //  { 
        //    case CommandVisualBehaviourEnum.ShowAsActive:
        //      //SetActiveApplication(c.TargetApplicationName);
        //      XapExplorer.Commands.Add(
        //        new Command()
        //        {
        //          CommandType = CommandTypeEnum.ActivateApp,
        //          TargetApplicationName = c.TargetApplicationName
        //        }.ToJson()
        //      );
        //      break;

        //    case CommandVisualBehaviourEnum.ShowAsPreviousApp:
        //      //SetPreviousActiveApplication(c.TargetApplicationName);
        //      XapExplorer.Commands.Add(
        //        new Command()
        //        {
        //          CommandType = CommandTypeEnum.ActivateAppAsPrevious,
        //          TargetApplicationName = c.TargetApplicationName
        //        }.ToJson()
        //      );
        //      break;
        //  }


      }

      

      //foreach (string i in e.NewItems)
      //{
      //  Command c = Command.Parse(i);
      //  if ((int)c.CommandType < 255)
      //  {
      //    doCmd(c);
      //    e.NewItems.RemoveAt(e.NewItems.IndexOf(i));
      //  }
      //}
    }




    private static void processSystemCommand(Command c)
    {
      switch (c.CommandType)
      {
        case CommandTypeEnum.BeginWait:
          _instance.AppRunBtn.IsBusy = true;
          break;

        case CommandTypeEnum.ActivateApp:
          //SetActiveApplication(c);
          if (_appValidator.ContainsKey(c.TargetApplicationName))
            _appValidator[c.TargetApplicationName].IsActive = true;

          break;

        case CommandTypeEnum.ActivateAppAsPrevious:
          //SetPreviousActiveApplication(c.TargetApplicationName);
          if (_appValidator.ContainsKey(c.TargetApplicationName))
            SetPreviousActiveApplication(_appValidator[c.TargetApplicationName]);

          break;

        case CommandTypeEnum.EndWait:
          _instance.AppRunBtn.IsBusy = false;
          break;

        case CommandTypeEnum.RunApplication:
          RunApplication(c.TargetApplicationName);
          break;

        case CommandTypeEnum.StopApplication:
          StopApplication(c.TargetApplicationName);
          break;

        case CommandTypeEnum.ChangeAppTabBtnLabel:
          ChangeAppLabel(c.TargetApplicationName, c.CommandParameter);
          break;

        case CommandTypeEnum.ActivateCommunication:
          if (_pendingCommands.ContainsKey(c.CommandParameter))
          {
            Command appCmd = _pendingCommands[c.CommandParameter];
            _pendingCommands.Remove(c.CommandParameter);
            Commands.Add(appCmd.ToJson());
            //processCmdAsync(appCmd);
          }
          break;
      }


    }




    private static void zmenaVelikostiAplikace(Object sender, SizeChangedEventArgs e)
    {
      zmenaPoctuAplikaci(null, null);
    }





    private static void zmenaPoctuAplikaci(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
      if (RunningApplications.Count == 0)
      {
        _instance.showAppMenu(null, null);
        return;
      }

      //if any hooked event handler to ResizeTabButtons event
      //and appTabsControl exists, raise event
      if (ResizeTabButtons != null && appTabsControl != null)
        ResizeTabButtons(null, new ResizeEventArgs(ComputedTabButtonWidth));
      
    }





    public static double ComputedTabButtonWidth
    {
      get { return appTabsControl.RenderSize.Width / (double)RunningApplications.Count; }
    }



    /// <summary>
    /// viewport of <c>Grid</c> UI element that is used to drive running apps.
    /// TabButton is inserted here for each running app
    /// </summary>
    private static Grid appTabsControl
    {
      get { return _instance.AppTabContainer; } 
    }





    /// <summary>
    /// viewport of <c>Grid</c> UI element that is used to view running apps.
    /// downloaded <c>UserControl</c> is inserted here for each running app
    /// </summary>
    public static Grid AppViewportControl
    {
      get { return _instance.AppViewPort; }
    }






    ///// <summary>
    ///// busy control UI element that is used to block app GUI.
    ///// </summary>
    //public static BusyIndicator AppBusyControl
    //{ get; set; }





    /// <summary>
    /// readonly collection of running applications
    /// </summary>
    public static ReadOnlyObservableCollectionPlus<InteractiveApp> RunningApplications
    { get; private set; }






    /// <summary>
    /// previous active app on screen
    /// </summary>
    private static InteractiveApp previousActiveApplication
    { get; set; }





    /// <summary>
    /// current active app on screen
    /// </summary>
    public static InteractiveApp ActiveApplication
    { get; private set; }





    ///// <summary>
    ///// readonly collection of running applications
    ///// </summary>
    //public static ObservableCollection<AppInfo> AvailableApplications
    //{ get; set; }






    /// <summary>
    /// get environment variables. This is used to exchange data between various applications.
    /// For safety reason, all values must be converted to string before store into Dictionary. Use JSON when complex type required.
    /// </summary>
    public static Dictionary<string, string> Environment
    { get; private set; }




    /// <summary>
    /// pending commands collection is reserved for aplication that were loaded on demand of another app
    /// asynchronously and after load, they might ask to get last command...
    /// </summary>
    private static Dictionary<string, Command> _pendingCommands;




    /// <summary>
    /// get collection of commands. This is used to exchange data between various applications
    /// </summary>
    public static CommandStack Commands
    { get; private set; }





    private static void RunApplication(string appName)
    {
      if (string.IsNullOrEmpty(appName)) //invalid call, ignore
        return;

      AvailableApplications apps = (AvailableApplications)_instance.SeznamAplikaci.Resources["AvailableApplications"];
      foreach (AppInfo app in apps)
      {
        if (app.Name == appName)
          RunApplication(app);
      }

    }




    /// <summary>
    /// inserts new app into viewport
    /// </summary>
    /// <param name="app">new app to insert</param>
    private static void RunApplication(AppInfo appInfo)
    {
      InteractiveApp app;

      //resolve load duplicity...
      if (_appValidator.ContainsKey(appInfo.Name))
      {
        //if allready loaded, focus to running instance...
        app = _appValidator[appInfo.Name];

        //app.IsActive = true;

      }
      else
      {
        //start load..
        AppLoader loader = new AppLoader();

        app = new InteractiveApp()
        {
          Application = loader,
          AppInfo = appInfo
        };

        loader.Visibility = Visibility.Collapsed;
        XapExplorer.AppViewportControl.Children.Add(loader);

        loader.LoadApp(app);

        _appValidator.Add(appInfo.Name, app);

        //add to visual tree...
        _applications.Add(app);

        //set new app as active one
        //app.IsActive = true;
      }

      _instance.showApps(null, null);

    }


    

    /// <summary>
    /// stops running app give by index
    /// </summary>
    /// <param name="index">index of app to stop</param>
    private static void StopApplication(int index)
    {
      StopApplication(_applications[index]);
    }




    /// <summary>
    /// stops running app given by name
    /// </summary>
    /// <param name="appName">name of app to stop</param>
    private static void StopApplication(string appName)
    {
      if (string.IsNullOrEmpty(appName) || !_appValidator.ContainsKey(appName)) //invalid call, ignore
        return;

      StopApplication(_appValidator[appName]);
    }




    /// <summary>
    /// stops running app
    /// </summary>
    /// <param name="app">app to stop</param>
    private static void StopApplication(InteractiveApp app)
    {

      //delete al pending commands for currently stoped app
      if (_pendingCommands.ContainsKey(app.AppInfo.Name))
        _pendingCommands.Remove(app.AppInfo.Name);

      ////try send PrepareStopApplication command to stopped app...
      //Commands.Add(
      //  new Command()
      //  {
      //    CommandType = CommandTypeEnum.PrepareStopApplication,
      //    CommandParameter = app.AppInfo.Name
      //  }.ToJson()
      //);

      //remove app gui...
      AppViewportControl.Children.Remove(app.Application);

      //if app is previous app...
      if(previousActiveApplication != null)
        if (previousActiveApplication.Equals(app))
          previousActiveApplication = null;

      //if removing active
      if (ActiveApplication.Equals(app) && _applications.Count > 1)
      {
        //reset active app first
        ActiveApplication = null;

        //get visual index of removed active app ...
        int index = _applications.IndexOf(app);

        //resolve index for next active...
        if (index == (_applications.Count - 1))
          index--;
        else
          index++;

        //activate new active app..
        _applications[index].IsActive = true;
      }
      //app.IsActive = false;
      //ActiveApplication.IsActive = true;

      //remove app from control collections
      _applications.Remove(app);
      _appValidator.Remove(app.AppInfo.Name);

      //resolve ActiveApplication ...
      if (_applications.Count == 0)
        ActiveApplication = null;

    }





    //private static void SetPreviousActiveApplication(string appName)
    //{
    //  if (!_appValidator.ContainsKey(appName))
    //    return;

    //  SetPreviousActiveApplication(_appValidator[appName]);
    //}




    
    private static void SetPreviousActiveApplication(InteractiveApp app)
    {

      if (ActiveApplication == null)
      {
        app.IsActive = true;
        return;
      }

      if (app == ActiveApplication)
        return;

      if (app.AppInfo.AppType == AppTypeEnum.Vyhledavani)
        return;

      if (previousActiveApplication != null)
        previousActiveApplication.Application.Visibility = Visibility.Collapsed;

      app.Application.Visibility = Visibility.Visible;
      app.Application.SetValue(Canvas.ZIndexProperty, 0);
      ActiveApplication.Application.SetValue(Canvas.ZIndexProperty, 10);
      previousActiveApplication = app;

    }




    ///// <summary>
    ///// this is reserved call for commands
    ///// </summary>
    ///// <param name="c">Command with commandType ActivateApp</param>
    //private static void SetActiveApplication(Command c)
    //{ 
    //  if(c.CommandType == CommandTypeEnum.ActivateApp && c.TargetApplicationName != "")
    //    //SetActiveApplication(c.TargetApplicationName);
    //    _appValidator[c.TargetApplicationName].IsActive = true;

    //}





    public static void SetActiveApplication(string appName)
    {
      //SetActiveApplication(_appValidator[appName]);
      if (_appValidator.ContainsKey(appName))
        SetActiveApplication(_appValidator[appName]);
        //_appValidator[appName].IsActive = true;
    }




    /// <summary>
    /// XapExplorer sets new active application. Application will display on top
    /// and receive Focus() event.
    /// </summary>
    /// <param name="app"></param>
    private static void SetActiveApplication(InteractiveApp app)
    {
      //XapExplorer.ActiveApplication = app;

      if (app == ActiveApplication)
        return;

      if (previousActiveApplication != null)
        previousActiveApplication.Application.Visibility = Visibility.Collapsed;

      //resolve active...
      if (ActiveApplication != null)
      {
        if (ActiveApplication.AppInfo.AppType == AppTypeEnum.Vyhledavani)
        {
          ActiveApplication.Application.Visibility = Visibility.Collapsed;
          previousActiveApplication = null;
        }
        else
        {
          ActiveApplication.Application.SetValue(Canvas.ZIndexProperty, 0);
          previousActiveApplication = ActiveApplication;
        }
      }

      app.Application.Visibility = Visibility.Visible;
      //app.IsActive = true;
      ActiveApplication = app;

      app.Application.SetValue(Canvas.ZIndexProperty, 10);
      app.Application.Focus();

    }




    private static void ChangeAppLabel(string targetAppname, string newLabel)
    {
      if(_appValidator.ContainsKey(targetAppname))
      {
        InteractiveApp app = _appValidator[targetAppname];
        app.Label = newLabel;
      }
    }




    private static void _resolveActiveRemove(InteractiveApp app)
    {

      if (_applications.Count == 1)
      {
        ActiveApplication = null;
        return;
      }

      if (!ActiveApplication.Equals(app))
        return;

      //user removes active app...
      int index = _applications.IndexOf(app);

      //if remove active app, resolve next active...
      if (index == (_applications.Count - 1))
        ActiveApplication = _applications[index - 1];
      else
        ActiveApplication = _applications[index + 1];

      //app.IsActive = false;
      ActiveApplication.IsActive = true;

    }



    //private static System.Windows.Threading.DispatcherTimer myDispatcherTimer;
    //private static Command tmpCmd;

    //private static void processCmdAsync(Command c)
    //{
    //  tmpCmd = c;
    //  myDispatcherTimer = new System.Windows.Threading.DispatcherTimer();
    //  myDispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, 500); // 500 Milliseconds 
    //  myDispatcherTimer.Tick += new EventHandler(myDispatcherTimer_Tick);
    //  myDispatcherTimer.Start();
    //}




    //// Raised every 100 miliseconds while the DispatcherTimer is active.
    //private static void myDispatcherTimer_Tick(object o, EventArgs sender)
    //{
    //  myDispatcherTimer.Stop();
    //  myDispatcherTimer = null;
    //  Commands.Add(tmpCmd.ToJson());
    //}


  }

}
